package com.XSQS.storage.index;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;

import com.XSQS.storage.util.XMLSubTree;
import com.XSQS.util.Utility;

public class InvertedMerger {
	
	private Logger logger=Utility.getLogger(InvertedMerger.class);
	
	private static InvertedMerger instance;
	
	private InvertedCreater creater;
	private InvertedReader reader;
	private InvertedWriter writer;
	
	private InvertedMerger(){
		creater=InvertedCreater.getInstance();
		reader=InvertedReader.getInstance();
		writer=InvertedWriter.getInstance();
	}
	
	public synchronized static InvertedMerger getInstance(){
		if(instance==null){
			instance=new InvertedMerger();
		}
		return instance;
	}
	
	public void merge2Index(XMLSubTree xmlSubTree){
		Map<String,List<IndexEntry>> itemMap=creater.createItemMap(xmlSubTree);
		Set<String> keySet=itemMap.keySet();
		String[] keys = new String[keySet.size()];
		keys= keySet.toArray(keys);
		Arrays.sort(keys);
		
		reader.open();
		writer.open();
		InvertedWordItem[] oldItems=reader.readAllInvertedWordItem();
		InvertedWordItem[] items=new InvertedWordItem[oldItems.length+keys.length];
		System.arraycopy(oldItems, 0, items, 0, oldItems.length);
		
		int currentLen=oldItems.length;
		oldItems=items;
		
		int entryPos=reader.getNumOfIndexEntries()+1;
		
		InvertedWordItem keyItem = new InvertedWordItem();
		int fromIndex=0;
		for(int i=0;i<keys.length;++i){
			List<IndexEntry> entries=itemMap.get(keys[i]);
			keyItem.setKey(keys[i]);
			int hit;
			if(fromIndex<currentLen){
				hit = Arrays.binarySearch(items,fromIndex,currentLen,keyItem);
			}else{
				hit=-currentLen;
			}
			if(hit>=0){
				int lastEntryIndex=oldItems[hit].getLastEntry();
				IndexEntry lastEntry=reader.readIndexEntry(lastEntryIndex);
				lastEntry.setNext(entryPos);
				writer.writeIndexEntry(lastEntryIndex, lastEntry);
				oldItems[hit].setLastEntry(entryPos+entries.size()-1);
				fromIndex=hit;
			}else{
				int insertIndex=-hit-1;
				System.arraycopy(items, insertIndex, items, insertIndex+1, currentLen-insertIndex);
				items[insertIndex]=new InvertedWordItem();
				items[insertIndex].setKey(keys[i]);
				items[insertIndex].setFirstEntry(entryPos);
				items[insertIndex].setLastEntry(entryPos+entries.size()-1);
				++currentLen;
				fromIndex=insertIndex+1;
			}
			
			for(int j=0;j<entries.size()-1;++j){
				entries.get(j).setNext(++entryPos);
			}
			entries.get(entries.size()-1).setNext(InvertedUtil.NULL);
			writer.writeIndexEntries(entries);
			if((++entryPos)>=Integer.MAX_VALUE){
				logger.error("The size of IndexEntry is out of Integer.MAX_VALUE.");
			}
		}
		writer.rewriteWordItems(items,0,currentLen);
		reader.close();
		writer.close();
	}
}
